package net.vanchu.mortgagecalculator.mortgage.data.calculator;

import net.vanchu.mortgagecalculator.common.config.LoanWay;
import net.vanchu.mortgagecalculator.mortgage.data.PrincipalAndInterest;
import net.vanchu.mortgagecalculator.mortgage.data.loan.AbsLoan;
import net.vanchu.mortgagecalculator.mortgage.data.loan.WithCommercial;
import net.vanchu.mortgagecalculator.mortgage.data.loan.WithProvidentFund;
import net.vanchu.mortgagecalculator.mortgage.data.monthRepayment.SingleMonthRepay;
import net.vanchu.mortgagecalculator.mortgage.data.repaymentSummary.PrincipalBalancedPart;
import net.vanchu.mortgagecalculator.mortgage.data.repaymentSummary.RepaySummary;
import net.vanchu.mortgagecalculator.mortgage.data.repaymentSummary.TotalBalancedPart;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Granton on 2016/12/20.
 */
public class SingleCalculator {

    public static RepaySummary.Single calRepaySummary(AbsLoan absLoan) {
        boolean wayCommercial = absLoan instanceof WithCommercial;
        return calRepaySummary(absLoan, wayCommercial);
    }

    public static RepaySummary.Single calRepaySummary(AbsLoan absLoan, boolean wayCommercial) {
        int numberOfMonths = absLoan.getYear() * 12;
        double principal;
        double discount;
        double yearRate;
        LoanWay loanWay = wayCommercial ? LoanWay.wayCommercial : LoanWay.wayProvidentFund;
        if (wayCommercial) {
            WithCommercial withCommercial = (WithCommercial) absLoan;
            principal = withCommercial.getCommercialPrincipal();
            discount = withCommercial.getCommercialDiscount();
            yearRate = withCommercial.getCommercialInterestRate();
        } else {
            WithProvidentFund withProvidentFund = (WithProvidentFund) absLoan;
            principal = withProvidentFund.getProvidentFundPrincipal();
            discount = withProvidentFund.getProvidentFundDiscount();
            yearRate = withProvidentFund.getProvidentFundInterestRate();
        }
        double monthRate = yearRate / 12 * discount;
        return new RepaySummary.Single(loanWay, numberOfMonths, principal,
                calTotalBalancedPart(numberOfMonths, principal, monthRate),
                calPrincipalBalancedPart(numberOfMonths, principal, monthRate));
    }

    public static TotalBalancedPart.Single calTotalBalancedPart(int numberOfMonths, double principal, double yearRate) {
        double totalValueToPay = 0;
        double alreadyCalPrincipal = 0;
        double repayPerMonth = TBalancedCalculator.calRepayPerMonth(principal, yearRate, numberOfMonths);
        List<SingleMonthRepay> monthRepays = new ArrayList<>();
        for (int index = 0; index < numberOfMonths; index++) {
            double interestToPay = TBalancedCalculator.calInterestWithIndex(principal, yearRate, repayPerMonth, index);
            double principalToPay = TBalancedCalculator.calPrincipalOneMonthToPay(repayPerMonth, interestToPay);
            double remainPrincipal = principal - alreadyCalPrincipal - principalToPay;
            SingleMonthRepay monthRepay = new SingleMonthRepay(
                    index + 1, remainPrincipal, principalToPay, interestToPay);
            totalValueToPay += monthRepay.getTotalRepayNumber();
            alreadyCalPrincipal += monthRepay.getTotalPrincipal();
            monthRepays.add(monthRepay);
        }
        double interestTotalToPay = totalValueToPay - principal;

        return new TotalBalancedPart.Single(repayPerMonth, monthRepays,
                new PrincipalAndInterest(principal, interestTotalToPay));
    }

    private static PrincipalBalancedPart.Single calPrincipalBalancedPart(int numberOfMonth, double principal, double yearRate) {
        double numberFirstMonth = PBalancedCalculator.calFirstRepayNumber(principal, yearRate, numberOfMonth);
        double decreaseNumber = PBalancedCalculator.calDecreaseNumber(principal, yearRate, numberOfMonth);
        double totalInterest = PBalancedCalculator.calTotalInterest(principal, yearRate, numberOfMonth);
        double principalPerMonth = PBalancedCalculator.calPrincipalPerMonth(principal, numberOfMonth);
        double remainPrincipal = principal;
        List<SingleMonthRepay> monthRepays = new ArrayList<>();
        for (int index = 0; index < numberOfMonth; index++) {
            double interestToPay = PBalancedCalculator.calInterestCurrentMonth(
                    remainPrincipal, yearRate);
            remainPrincipal = PBalancedCalculator.calRemainPrincipal(
                    principal, principalPerMonth, index + 1);
            SingleMonthRepay singleMonthRepay = new SingleMonthRepay(
                    index + 1, remainPrincipal, principalPerMonth, interestToPay);
            monthRepays.add(singleMonthRepay);
        }
        return new PrincipalBalancedPart.Single(decreaseNumber, numberFirstMonth, monthRepays,
                new PrincipalAndInterest(principal, totalInterest));
    }
}